#pragma once

#include <iostream>
#include <sstream>
#include <fstream>
#include <random>
#include <iomanip>
#include <atomic>

#include <cstdlib>
#include <cstring>

#include <sqlite3.h>

#include "log.hpp"

#include <sys/stat.h>

namespace mymq
{
    class SqliteHelper
    {
    public: 
        typedef int(*SqliteCallback)(void*, int, char**, char**);
    public:
        SqliteHelper(const std::string& dbfile)
        : _dbfile(dbfile)
        , _handler(nullptr)
        {}
        bool Open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE| SQLITE_OPEN_CREATE| safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                FATAL_LOG("创建/打开sqlite数据库失败:%s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool Exec(const char* sql, const SqliteCallback& cb = nullptr, void* arg = nullptr)
        {
            int ret = sqlite3_exec(_handler, sql, cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                INFO_LOG("%s 执行语句失败：%s", sql, sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        void Close()
        {
            sqlite3_close_v2(_handler);
            _handler = nullptr;
        }
        ~SqliteHelper()
        {
            if (_handler == nullptr) return;
            Close();
        }
    private:
        std::string _dbfile;
        sqlite3* _handler;
    };

    class StrHelper
    {
    public:
        static size_t Split(const std::string& str, const std::string& sep, std::vector<std::string>* result)
        {
            size_t pos = 0, idx = 0;
            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    result->emplace_back(str.substr(idx));
                    break;
                }
                if (pos > idx)
                    result->emplace_back(str.substr(idx, pos - idx));
                idx = pos + sep.size();
            }
            return result->size();
        }
    };

    class UUIDHelper
    {
    public:
        static std::string Getuuid()
        {
            std::random_device rd;
            std::mt19937_64 gernator(rd());
            std::uniform_int_distribution<int> distribution(0, 255);
            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::hex << std::setw(2) << std::setfill('0') << distribution(gernator);
                if (i == 3 || i == 5 || i == 7) ss << "-";
            }
            static std::atomic<size_t> seq(1);
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                ss << std::hex << std::setw(2) << std::setfill('0') << ((num >> i * 8) &0xff);
                if (i == 6) ss << "-";
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string filename)
        : _filename(filename)
        {}
        bool Exists()
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0;
        }
        size_t Size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0) return 0;
            return st.st_size;
        }
        bool Rename(const std::string& new_name)
        {
            // 如果 _filename不存在，rename()会失败（返回 -1）
            return ::rename(_filename.c_str(), new_name.c_str()) == 0;
        }
        bool Read(char* buf, size_t offset, size_t len)
        {
            std::ifstream ifs(_filename, std::ios::binary| std::ios::in);
            if (!ifs.is_open())
            {
                ERROR_LOG("%s文件打开失败!", _filename.c_str());
                return false;
            }
            ifs.seekg(offset, std::ios::beg);
            ifs.read(buf, len);
            if (!ifs.good())
            {
                ERROR_LOG("%s文件读取失败!", _filename.c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        bool Read(std::string* buf)
        {
            size_t fsize = Size();
            buf->resize(fsize);
            return Read(&(*buf)[0], 0, fsize);
        }
        bool Write(const char* buf, size_t offset, size_t len)
        {
            std::fstream fs(_filename, std::ios::binary| std::ios::in| std::ios::out);
            if (!fs.is_open())
            {
                ERROR_LOG("%s文件打开失败!", _filename.c_str());
                return false;
            }
            fs.seekp(offset, std::ios::beg);
            fs.write(buf, len);
            if (!fs.good())
            {
                ERROR_LOG("%s文件写入失败!", _filename.c_str());
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }
        bool Write(const std::string& buf)
        {
            return Write(buf.c_str(), 0, buf.size());
        }
        static std::string ParentDirectory(const std::string &filename)
        {
            // /a/b/c/d/text.txt
            size_t pos = filename.find_last_of('/');
            if (pos == std::string::npos) return "./";
            return filename.substr(0, pos);
        }
        static bool CreateFile(const std::string& filename)
        {
            std::ofstream ofs(filename, std::ios::binary| std::ios::out);
            if (!ofs.is_open())
            {
                ERROR_LOG("%s文件创建失败!", filename.c_str());
                return false;
            }
            ofs.close();
            return true;
        }
        static bool RemoveFile(const std::string& filename)
        {
            return ::remove(filename.c_str()) == 0;
        }
        static bool CreateDirectory(const std::string& path)
        {
            // a/b/c/d
            size_t pos = 0, idx = 0;
            while (idx < path.size())
            {
                pos = path.find('/', idx);
                if (pos == std::string::npos)
                {
                    return ::mkdir(path.c_str(), 0775) == 0;
                }
                std::string subpath = path.substr(0, pos);
                int ret = ::mkdir(subpath.c_str(), 0775);
                if (ret != 0 && errno != EEXIST)
                {
                    ERROR_LOG("%s目录创建失败,%s", subpath.c_str(), ::strerror(errno));
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }
        static bool RemoveDirectory(const std::string& path)
        {
            std::string cmd = "rm -rf " + path;
            return ::system(cmd.c_str()) != -1;
        }
    private:
        std::string _filename;
    };
}